// ========================================================================
// Copyright (c) 2022 ChangYou, All rights reserved.
// http://www.changyou.com/
// 
// Filename:    FoliageIntsanceObject.cs
// Time:        2022-08-31 13:05:36
// Author:      xiangjinbao
// Email:       xiangjinbao@cyou-inc.com
// Version:     v2022.0.1
// Description: FoliageIntsanceObject
// ========================================================================

using System;
using System.Collections.Generic;
using UnityEngine;

namespace ProjectTW.Foliage
{
    [Serializable]
    public class FoliageInstanceObject
    {
        public const int Area_Size = FoliageManager.Foliage_Instance_Area_Size;

        [SerializeField]
        private string m_Guid;
        [SerializeField]
        private bool _Enabled = true;
        [SerializeField]
        internal Vector3 m_Pos = -Vector3.one;
        [SerializeField]
        private FoliageResolutions m_FoliageAreaResolution = FoliageResolutions._512;
        [SerializeField]
        private FoliageWorldMaps m_WorldMaps;
        [SerializeField]
        private FoliageDensityMap m_DensityMap;

        [SerializeField]
        private float m_TransformCordsMultiplier = -1;

        [SerializeField]
        private FoliageChunk m_AttachedTo;

        private int m_FoliageAreaResolutionIntegral = -1;
        private int m_LastDensityMapsFetchingFrame;
        private Dictionary<int, FoliageGPUMesh> m_MeshInstances;
        
        public string Guid
        {
            get
            {
                if (m_Guid == null)
                {
                    m_Guid = System.Guid.NewGuid().ToString();
                }

                return m_Guid;
            }
        }

        public new bool Enabled
        {
            get
            {
                return _Enabled;
            }
            set
            {
                if (_Enabled == value)
                {
                    return;
                }

                _Enabled = value;
                FoliageMeshManager.RegenerateQueueInstances();
            }
        }

        public Dictionary<int, FoliageGPUMesh> MeshInstances
        {
            get
            {
                if (m_MeshInstances == null)
                {
                    PoolMeshInstances();
                }

                return m_MeshInstances;
            }
        }

        public Vector3 Pos
        {
            get
            {
                return m_Pos;
            }
        }

        public bool IsEmpty
        {
            get
            {
                int res = FoliageAreaResolutionIntegral;
                List<FoliagePrototype> prototypes = FoliageDB.UnSortedPrototypes;

                for (int x = 0; x < res; x++)
                {
                    for (int z = 0; z < res; z++)
                    {
                        for (int i = 0; i < prototypes.Count; i++)
                        {
                            if (DensityMap.GetPrototypeDensity(x, z, prototypes[i].Id) != 0)
                            {
                                return false;
                            }
                        }
                    }
                }

                return true;
            }
        }

        public FoliageResolutions FoliageAreaResolution
        {
            get
            {
                return m_FoliageAreaResolution;
            }
            set
            {
                if (m_FoliageAreaResolution != value)
                {
                    m_FoliageAreaResolution = value;
                    m_FoliageAreaResolutionIntegral = (int) value;
                    m_TransformCordsMultiplier = -1;
                    UpdateResolutionChange();
                }
            }
        }

        public int FoliageAreaResolutionIntegral
        {
            get
            {
                if (m_FoliageAreaResolutionIntegral == -1)
                {
                    m_FoliageAreaResolutionIntegral = (int) FoliageAreaResolution;
                }

                return m_FoliageAreaResolutionIntegral;
            }
        }

        public float TransformCordsMultiplier
        {
            get
            {
                if (m_TransformCordsMultiplier == -1.0f)
                {
                    m_TransformCordsMultiplier = (float) Area_Size / FoliageAreaResolutionIntegral;
                }

                return m_TransformCordsMultiplier;
            }
            set
            {
                m_TransformCordsMultiplier = value;
            }
        }

        /// <summary>
        /// 世界图
        /// </summary>
        public FoliageWorldMaps WorldMaps
        {
            get
            {
                if (m_WorldMaps == null)
                {
                    m_WorldMaps = FoliageMapUtility.GenerateWorldMaps(AttachedTo);
                }

                return m_WorldMaps;
            }
            set
            {
                m_WorldMaps = value;
            }
        }

        /// <summary>
        /// 密度图
        /// </summary>
        public FoliageDensityMap DensityMap
        {
            get
            {
                if (m_DensityMap == null)
                {
                    m_DensityMap = FoliageMapUtility.CreateDensityMap(AttachedTo);
                }

                return m_DensityMap;
            }
        }
        
        /// <summary>
        /// 依附Chunk对象
        /// </summary>
        public FoliageChunk AttachedTo
        {
            get
            {
                return m_AttachedTo;
            }
        }

        /// <summary>
        /// 创建细节管理对象实例
        /// </summary>
        /// <param name="attachedTo"></param>
        /// <param name="poppulateHeights"></param>
        /// <returns></returns>
        public static FoliageInstanceObject CreateInstance(FoliageChunk attachedTo, bool poppulateHeights = true)
        {
            FoliageInstanceObject foliageInstanceObject = new FoliageInstanceObject();
            foliageInstanceObject.m_AttachedTo = attachedTo;
            foliageInstanceObject.m_Pos = attachedTo.transform.position;
            attachedTo.AttachedFoliageInstance = foliageInstanceObject;
            
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                UnityEditor.EditorUtility.DisplayProgressBar("Foliage", "Creating Manager Instance - Density Maps", 0.33f);
            }
#endif
            foliageInstanceObject.m_DensityMap = FoliageMapUtility.CreateDensityMap(attachedTo);

#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                UnityEditor.EditorUtility.DisplayProgressBar("Foliage", "Creating Manager Instance - World Maps", 0.67f);
            }
#endif
            foliageInstanceObject.m_WorldMaps = FoliageMapUtility.GenerateWorldMaps(attachedTo, poppulateHeights);

#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                UnityEditor.EditorUtility.ClearProgressBar();
            }
#endif
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                UnityEditor.EditorUtility.SetDirty(attachedTo);
                UnityEditor.SceneManagement.EditorSceneManager.MarkAllScenesDirty();
            }
#endif
            FoliageMeshManager.RegenerateQueueInstances();
            
            return foliageInstanceObject;
        }

        /// <summary>
        /// 清理实例资源
        /// </summary>
        /// <param name="foliageInstance"></param>
        public static void CleanUp(FoliageInstanceObject foliageInstance)
        {
            if (foliageInstance.m_DensityMap != null)
            {
                foliageInstance.m_DensityMap.Dispose();
            }

            if (foliageInstance.m_WorldMaps != null)
            {
                foliageInstance.m_WorldMaps.HeightMap.Dispose();
            }

            foliageInstance.m_WorldMaps = null;
            foliageInstance.m_DensityMap = null;
            Resources.UnloadUnusedAssets();

            FoliageMeshManager.RegenerateQueueInstances();
        }

        private void UpdateResolutionChange()
        {
#if UNITY_EDITOR
            UnityEditor.EditorUtility.DisplayProgressBar("Foliage", "Foliage is recalculating the new Foliage area resolution \nThat might take awhile...", 0.2f);
#endif
            WorldMaps = FoliageMapUtility.GenerateWorldMaps(AttachedTo);

            DisposeExistingDensityMaps();
            m_DensityMap = null;
            UpdateDensityMapsForMaterials(false);
            PoolMeshInstances();
            UNSettings.Log("Foliage finished updating the area resolution successfully!! New resolution : " + m_FoliageAreaResolutionIntegral);
#if UNITY_EDITOR
            UnityEditor.EditorUtility.ClearProgressBar();
#endif
        }

        private void DisposeExistingDensityMaps()
        {
            DensityMap.Dispose();
        }

        /// <summary>
        /// 将Mesh实例初始化到池中, 
        /// </summary>
        private void PoolMeshInstances()
        {
            if (FoliageManager.Instance == null)
            {
                return;
            }

            m_MeshInstances = FoliageManager.GetPrototypeMeshInstances(FoliageAreaResolution);
        }

        /// <summary>
        /// 更新材质属性块
        /// </summary>
        /// <param name="propertyBlock"></param>
        public void UpdateMaterialBlock(MaterialPropertyBlock propertyBlock)
        {
            propertyBlock.SetTexture("_HeightMap", WorldMaps.HeightMap.Map);

            // 512
            propertyBlock.SetFloat("_FoliageAreaResolution", FoliageAreaResolutionIntegral);
            // Pos
            propertyBlock.SetVector("_FoliageAreaPosition", m_Pos);
        }

        /// <summary>
        /// 更新草的图
        /// </summary>
        /// <param name="poppulateMapPixels"></param>
        internal void UpdateDensityMapsForMaterials(bool poppulateMapPixels)
        {
            if (DensityMap == null)
            {
                return;
            }

            if (poppulateMapPixels && DensityMap.MapPixels == null)
            {
            }
        }

        public int TransformCord(float x, float removeOffset)
        {
            return (int) TransformCordFloat(x, removeOffset);
        }

        public float TransformCordFloat(float x, float removeOffset)
        {
            return TransformCordCustomFloat(x, removeOffset, TransformCordsMultiplier);
        }

        public int TransformCordCustom(float x, float removeOffset, float multiplier)
        {
            return (int) TransformCordCustomFloat(x, removeOffset, multiplier);
        }

        public float TransformCordCustomFloat(float x, float removeOffset, float multiplier)
        {
            return (x - removeOffset) / multiplier;
        }

        public int InverseCord(float x, float addOffset)
        {
            return (int) InverseCordFloat(x, addOffset);
        }

        public float InverseCordFloat(float x, float addOffset)
        {
            return InverseCordCustomFloat(x, addOffset, TransformCordsMultiplier);
        }

        public int InverseCordCustom(float x, float addOffset, float multiplier)
        {
            return (int) InverseCordCustomFloat(x, addOffset, multiplier);
        }

        public float InverseCordCustomFloat(float x, float addOffset, float multiplier)
        {
            return (x * multiplier) + addOffset;
        }
    }
}
